package cn.cubix.flow.config;

import cn.cubix.flow.security.AuthEventHandler;
import cn.cubix.flow.security.AuthTokenFilter;
import cn.cubix.flow.security.CusPermissionEvaluator;
import cn.cubix.flow.security.SecurityService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Security配置类
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor(onConstructor=@__({@Autowired,@Lazy}))
public class CusSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AuthTokenFilter authTokenFilter;
    private final SecurityService securityService;
    /** 在PasswordEncoderConfig中指定了BCryptPasswordEncoder */
    private final PasswordEncoder passwordEncoder;
    private final AuthEventHandler authEventHandler;
    private final CusPermissionEvaluator cusPermissionEvaluator;

    @Bean
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 配置策略
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        // 由于使用的是JWT，不需要csrf
        httpSecurity.csrf().disable();
        // 基于token，所以不需要session
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 放行静态资源
        String[] staticPatterns = { "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/**/*.ico", "/**/assets/**",
                "/**/css/**", "/**/fonts/**", "/**/js/**", "/**/img/**", "/**/svg/**"};
        httpSecurity.authorizeRequests().antMatchers(HttpMethod.GET,staticPatterns).permitAll();
        // 放行接口
        httpSecurity.authorizeRequests().antMatchers(HttpMethod.POST,"/api/security/signin").permitAll();
        httpSecurity.authorizeRequests().antMatchers(HttpMethod.POST,"/api/security/signup").permitAll();
        httpSecurity.authorizeRequests().antMatchers(HttpMethod.POST,"/api/security/signout").permitAll();
        // 除上面外的所有请求全部需要鉴权认证
        httpSecurity.authorizeRequests().anyRequest().authenticated();
        httpSecurity.headers().frameOptions().disable();

        // 已登录用户访问无权限资源 handle
        httpSecurity.exceptionHandling().accessDeniedHandler(authEventHandler);
        // 登录失败、未登录访问无权限资源 commence
        httpSecurity.exceptionHandling().authenticationEntryPoint(authEventHandler);

        // 添加JWT验证过滤器
        httpSecurity.addFilterAt(authTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 权限校验
        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(this.cusPermissionEvaluator);
        httpSecurity.authorizeRequests().expressionHandler(expressionHandler);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
//        web.ignoring().antMatchers("/**/nacos/**");
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationConfigurer<AuthenticationManagerBuilder, SecurityService> daoAuthenticationConfigurer =
                auth.userDetailsService(securityService);
        daoAuthenticationConfigurer.passwordEncoder(passwordEncoder);
    }
}

